/**
 * 地磅串口转WebScoket
 * by 货当当
 * 版本V0.5
 * 版本日期 2021年12月25日
 * v0.5版本说明 
 *     + 用串口read()代替setInterval定时器
 * v0.4版本说明（2021年09月24日）
 *     + 把主文件拆为模块
 *     + 增加瑧识R3相机对应的开闸操作
 *  
 * v0.3版本说明（2021年09月23日）: 
 *     + 针对多磅多摄像头做了适配
 *     + 摄像头识别接口可接收客户端响应并放回当前所有车牌信息
 *     + 针对部分摄像头content-type不规范做了做了兼容
 *     + 优化numStack全局变量为局部变量
 */
const path         = require('path');
const https = require('https');
const fs = require('fs');
const express = require('express');
const cors = require('cors');
const app = express();
const multer  = require('multer');
const upload = multer();
const SerialPort = require('serialport');
const schedule = require('node-schedule');
const child_process = require('child_process');
const request = require('request');
const WebSocket = require('ws');

const {CompanyName,SerIP,SerDev,videoDev,PoundUri} = require('./config.js');// 引入配置
const {parser} = require('./parser.js'); // 引入地磅解析函数
const {barrier} = require('./barrier.js');

// ssl注入app
const httpsServ = https.createServer({
    key: fs.readFileSync('./'+SerIP+'cert.key','utf8'),
    cert: fs.readFileSync('./'+SerIP+'cert.pem','utf8'),
}, app);

// websocket注入app
const expressWs = require('express-ws')(app,httpsServ);

// app 跨域
app.use(cors());
//货主PC本地部署路径
app.use(express.static('shipper-dist/dist')); 
// 解析POST数据
app.use(express.json({type:["text/plain","*/json"]})); // type:plain参数针对部分content-type不规范的摄像头，如文通。
app.use(express.urlencoded({extended: false}));

/***ssl及websocket注入，下面可以写业务逻辑了***/

// 出错时统一回调函数
function errback(err) {
    if (err) {
      return console.log('Error: ', err.message)
    }
}

//git仓库同步，每5分钟更新
function scheduleCron() {
    if (!fs.existsSync('./shipper-dist')) {
        child_process.exec('d: && cd d:/dibang && git clone https://gitee.com/huodangdang/shipper-dist.git');
    }
    let rule = new schedule.RecurrenceRule();
    rule.minute = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55];
    schedule.scheduleJob(rule, function(){ 
        child_process.exec('d: && cd d:/dibang/shipper-dist && git pull', function (err, stdout, stderr) {  
            heartBeat()     
            fs.writeFile('./git.log', stdout, function (err) {
                if (err) console.log(err);
                console.log('git pull: success');
            });
        });   
    });
}
//服务器心跳检测
function heartBeat() {
    if(!CompanyName){
        return;
    }
    let url="https://api.huodangdang.com/shipper/v1/pound/heartBeat";
    let requestData = {  
        companyName: CompanyName
    };
    
    request({
        url: url,
        method: "POST",
        json: true,
        headers: {
            "content-type": "application/json",
        },
        body: requestData
    }, function(error, response, body) {
        if (!error && response.statusCode == 200) {
            console.log(body,'连接正常') // 请求成功的处理逻辑
        }
    }); 

}
scheduleCron();

// 获取磅秤的串口转ws
const getPound = function(devNum) {
    console.log('ok');
    let port = new SerialPort(SerDev[devNum]['dev'], SerDev[devNum]['options'],errback);
    let stack = []; // 此处把串口数据读到缓冲数组里。因为串口的特点，如果N个函数一起调用port.read会出错。

    // v0.2新增功能，用于监控 重连地磅
    port.on('close',function(){
        console.log('close');
        stack.length = 0;
     });

    SerDev[devNum]['restartHandler'] = function(){
        port.close(()=>{ 
            console.log('reconn');
            port.open(errback);
        });
    }

    return function(ws,req) {

        port.on('readable', function () {
            let buff = parser.decode( port.read() );
            if(buff!==false) stack.push(buff);
            
            if(stack.length>10) {
                stack.shift();
            }
            
            if (ws.readyState === ws.OPEN) {
                let w = {stable:false,weight:"0.00"};
                if(stack.length>0) {
                    w.weight = stack[stack.length-1]; // 不要用stack.pop()，防止多台电脑连一个磅时数据不一致。
                }
                if(stack.length>=3 
                    &&  stack[stack.length-1]==stack[stack.length-2]
                    && stack[stack.length-1]==stack[stack.length-3])
                {
                    w.stable = true;
                }

                ws.send(JSON.stringify(w));
            }

         });
    };
}

// 车牌识别函数 // v0.3 针对多磅多摄像头区分
const getVehicle = function() {
    let numStack = {}; 
    
    let postHandler = function(req,res) {
        let ipaddr = req.body.AlarmInfoPlate.ipaddr;
        let license = req.body.AlarmInfoPlate.result.PlateResult.license;
 
        if( (license.length == 7) || (license.length==8) ) {
            numStack[ipaddr] = { vehicleNo:license , camerIndex:videoDev[ipaddr] };
        }
        //console.log(numStack);
        return res.send('ok');
    };

    let wsHandler = function(ws,req) {
        let interval;
        // 连接成功后使用定时器定时向客户端发送数据，同时要注意定时器执行的时机，要在连接开启状态下才可以发送数据
        interval = setInterval(() => {
            if (ws.readyState === ws.OPEN) {
                for(let ip in numStack) {       
                    if(!numStack[ip].hasOwnProperty('expire')) {     
                        //console.log(numStack[ip]);
                        numStack[ip].expire = true;       
                        ws.send( JSON.stringify(numStack[ip]) );  
                    }
                }
            } else {
                clearInterval(interval)
            }
        }, 1000);

        ws.on('message', function(msg) { // 客户端请求，则把所有车牌号返回或删除某车牌
            if(msg=='HeartBeat') {
                return;
            } else if(msg=="getAllVehicle") {
                for(let ip in numStack) {                  
                    ws.send(JSON.stringify(numStack[ip]) );
                }
            } else if(msg.substr(0,16)=="deleteVehicleNo:") { //保存后的车牌删掉
                let no = msg.substr(16).toUpperCase();
                for(let ip in numStack) {                  
                    if(no == numStack[ip].vehicleNo.toUpperCase()) {
                        delete numStack[ip];
                    }
                }
            }
        });
    };

    let haikangHandler = function(req,res) {
        if(req.files['anpr.xml'] === undefined) {
            return res.send('ok');
        }

        let hkxml = req.files['anpr.xml'][0].buffer.toString();

        let ipaddr = hkxml.match(/<ipAddress>(.+)<\/ipAddress>/);
        let license = hkxml.match(/<licensePlate>(.+)<\/licensePlate>/);

        if( (ipaddr!==null) && (license!==null) ) {
            ipaddr = ipaddr[1];
            license = license[1].substr(1);
        } else {
            return;
        }
 
        if( (license.length == 7) || (license.length==8) ) {
            numStack[ipaddr] = { vehicleNo:license , camerIndex:videoDev[ipaddr] };console.log(numStack);
        }

        return res.send('ok');
    }

    return [postHandler , wsHandler , haikangHandler];
}

const getVehicleInstance = getVehicle();

// v0.2重连磅头
app.get('/',function(req,res){
    return res.send("hello dibang");
});

// 重连地磅
app.post('/weight/restartWeight',function(req,res){
    let key = req.body.poundNum;
    if( SerDev[key]!==undefined && SerDev[key]['restartHandler']!==null ) {
        SerDev[key]['restartHandler']();
    }
    return res.send("success");
});

// 开启闸机
app.post('/openBarrier',function(req,res){
    let poundNum = req.body.poundNum;
    let poundIp = 0;
    for(let ip in videoDev) {
        if(videoDev[ip] == poundNum) {
            poundIp = ip;
            break;
        }
    }

    if(poundIp === 0) {
        return res.send('fail');
    }

    barrier(poundIp).then(()=>{
        return res.send('success');
    }).catch(()=>{
        return res.send('fail');
    });

});

// 读取地磅数据的websocket接口
for(let k in SerDev) {
    app.ws(PoundUri[k], getPound(k));
}

// 代理臻识摄像头的画面
for(let ip in videoDev) {
    //let upstream = new WebSocket("wss://shipper.qa.huodangdang.com:8081/secondWeightWebsocket");
    let upstream = null;

    app.ws('/camera'+videoDev[ip] , function(ws , req) {
        if(upstream===null || upstream.readyState === WebSocket.CLOSED) {
            upstream = new WebSocket("ws://" + ip + ":9080/ws");
            upstream.onclose = function() {
                setTimeout(()=>{
                    let that = new WebSocket(this.url);
                    that.onclose = this.onclose;
                    that.onerror = this.onerror;
                    that.onmessage = this.onmessage;
                    this.onclose = this.onerror = this.onmessage = null;
                    upstream = that;
                    console.log('重连中',Date());
                } , 5000)
            }
        }

        upstream.onerror = function(){}

        upstream.on('message', function (data) {
            //console.log(data.toString())
            if (ws.readyState === ws.OPEN) {
                ws.send(data);
            }
        });
    })
}

app.post('/devicemanagement' , getVehicleInstance[0] ); // 接收摄像头推送的车牌号
app.ws('/vehicleRecoWebsocket' , getVehicleInstance[1] ); // 推送车牌号
app.post('/haikangcp', upload.fields([{ name: 'anpr.xml', maxCount: 1 }, { name: 'licensePlatePicture.jpg', maxCount: 1 },{ name: 'detectionPicture.jpg', maxCount: 1 }]), getVehicleInstance[2]);


httpsServ.listen(443,'0.0.0.0');
app.listen(444,'0.0.0.0');